//! Step models
//! 
//! This module defines the step interface and related structures,
//! corresponding to HttpRunner's IStep and StepResult.

use crate::error::Result;
use crate::models::{config::StepConfig, Value, ValueMap, Timestamp, now, Validatable};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;

/// Step types (corresponds to HttpRunner's StepType)
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum StepType {
    /// HTTP request step
    Request,
    /// API call step
    API,
    /// Nested test case step
    TestCase,
    /// Transaction step
    Transaction,
    /// Rendezvous step (synchronization point)
    Rendezvous,
    /// Think time step (delay)
    ThinkTime,
    /// WebSocket step
    WebSocket,
    /// Android UI automation step
    Android,
    /// iOS UI automation step
    IOS,
    /// Harmony UI automation step
    Harmony,
    /// Browser automation step
    Browser,
    /// Shell command step
    Shell,
    /// Custom function step
    Function,
    /// AI-generated step
    AIGenerated,
}

/// Interface for test steps (corresponds to HttpRunner's IStep)
#[async_trait]
pub trait IStep: Send + Sync {
    /// Get step name
    fn name(&self) -> &str;
    
    /// Get step type
    fn step_type(&self) -> StepType;
    
    /// Get step configuration
    fn config(&self) -> &StepConfig;
    
    /// Get mutable step configuration
    fn config_mut(&mut self) -> &mut StepConfig;
    
    /// Execute the step
    async fn execute(&self, context: &mut StepContext) -> Result<StepResult>;
    
    /// Validate the step configuration
    fn validate(&self) -> Result<()> {
        self.config().validate()
    }
    
    /// Get step ID
    fn id(&self) -> Uuid;
    
    /// Clone the step
    fn clone_step(&self) -> Box<dyn IStep>;
}

/// Step execution context
#[derive(Debug, Clone)]
pub struct StepContext {
    /// Session variables
    pub variables: ValueMap,

    /// HTTP client
    pub http_client: reqwest::Client,

    /// Current test case ID
    pub test_case_id: Uuid,

    /// Current session ID
    pub session_id: Uuid,

    /// Execution start time
    pub start_time: Timestamp,
}

/// Step execution result (corresponds to HttpRunner's StepResult)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StepResult {
    /// Step ID
    pub id: Uuid,
    
    /// Step name
    pub name: String,
    
    /// Step start time in milliseconds
    pub start_time: i64,
    
    /// Step type
    pub step_type: StepType,
    
    /// Whether the step succeeded
    pub success: bool,
    
    /// Step execution time in milliseconds
    pub elapsed: i64,
    
    /// HTTP statistics (if applicable)
    pub http_stat: Option<HashMap<String, i64>>,
    
    /// Step execution data
    pub data: Option<Value>,
    
    /// Response content size in bytes
    pub content_size: i64,
    
    /// Extracted variables
    pub export_vars: ValueMap,
    
    /// Action results (for UI automation)
    pub actions: Vec<ActionResult>,
    
    /// Additional attachments (screenshots, logs, etc.)
    pub attachments: Option<Value>,
    
    /// Error message (if step failed)
    pub error: Option<String>,
    
    /// Validation results
    pub validation_results: Vec<ValidationResult>,
}

/// Action result for UI automation steps
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ActionResult {
    /// Action ID
    pub id: Uuid,
    
    /// Action type
    pub action_type: String,
    
    /// Action start time in milliseconds
    pub start_time: i64,
    
    /// Action elapsed time in milliseconds
    pub elapsed: i64,
    
    /// Whether the action succeeded
    pub success: bool,
    
    /// Action error message
    pub error: Option<String>,
    
    /// Action data
    pub data: Option<Value>,
    
    /// Screenshots or other attachments
    pub attachments: Vec<Attachment>,
}

/// Validation result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationResult {
    /// Validator name
    pub validator: String,
    
    /// Whether validation passed
    pub passed: bool,
    
    /// Expected value
    pub expected: Option<Value>,
    
    /// Actual value
    pub actual: Option<Value>,
    
    /// Error message (if validation failed)
    pub error: Option<String>,
}

/// Attachment (screenshot, log file, etc.)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Attachment {
    /// Attachment type
    pub attachment_type: AttachmentType,
    
    /// File name
    pub name: String,
    
    /// File path or URL
    pub path: String,
    
    /// File size in bytes
    pub size: u64,
    
    /// MIME type
    pub mime_type: String,
    
    /// Creation timestamp
    pub created_at: Timestamp,
}

/// Attachment types
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AttachmentType {
    Screenshot,
    Video,
    Log,
    Report,
    Data,
    Other(String),
}

impl StepResult {
    /// Create a new step result
    pub fn new(id: Uuid, name: String, step_type: StepType) -> Self {
        let now = now();
        Self {
            id,
            name,
            start_time: now.timestamp_millis(),
            step_type,
            success: false,
            elapsed: 0,
            http_stat: None,
            data: None,
            content_size: 0,
            export_vars: HashMap::new(),
            actions: Vec::new(),
            attachments: None,
            error: None,
            validation_results: Vec::new(),
        }
    }
    
    /// Mark the step as successful
    pub fn success(mut self) -> Self {
        self.success = true;
        self
    }
    
    /// Mark the step as failed with error message
    pub fn failure(mut self, error: String) -> Self {
        self.success = false;
        self.error = Some(error);
        self
    }
    
    /// Set elapsed time
    pub fn with_elapsed(mut self, elapsed: i64) -> Self {
        self.elapsed = elapsed;
        self
    }
    
    /// Set execution data
    pub fn with_data(mut self, data: Value) -> Self {
        self.data = Some(data);
        self
    }
    
    /// Set exported variables
    pub fn with_export_vars(mut self, vars: ValueMap) -> Self {
        self.export_vars = vars;
        self
    }
    
    /// Add validation result
    pub fn add_validation_result(&mut self, result: ValidationResult) {
        let passed = result.passed;
        self.validation_results.push(result);
        if !passed {
            self.success = false;
        }
    }
    
    /// Add action result
    pub fn add_action_result(&mut self, action: ActionResult) {
        self.actions.push(action);
    }
    
    /// Check if all validations passed
    pub fn all_validations_passed(&self) -> bool {
        self.validation_results.iter().all(|v| v.passed)
    }
    
    /// Get total validation count
    pub fn validation_count(&self) -> usize {
        self.validation_results.len()
    }
    
    /// Get failed validation count
    pub fn failed_validation_count(&self) -> usize {
        self.validation_results.iter().filter(|v| !v.passed).count()
    }
}

impl ActionResult {
    /// Create a new action result
    pub fn new(action_type: String) -> Self {
        let now = now();
        Self {
            id: Uuid::new_v4(),
            action_type,
            start_time: now.timestamp_millis(),
            elapsed: 0,
            success: false,
            error: None,
            data: None,
            attachments: Vec::new(),
        }
    }
    
    /// Mark the action as successful
    pub fn success(mut self) -> Self {
        self.success = true;
        self
    }
    
    /// Mark the action as failed
    pub fn failure(mut self, error: String) -> Self {
        self.success = false;
        self.error = Some(error);
        self
    }
    
    /// Set elapsed time
    pub fn with_elapsed(mut self, elapsed: i64) -> Self {
        self.elapsed = elapsed;
        self
    }
    
    /// Add attachment
    pub fn add_attachment(&mut self, attachment: Attachment) {
        self.attachments.push(attachment);
    }
}

impl ValidationResult {
    /// Create a successful validation result
    pub fn success(validator: String, expected: Option<Value>, actual: Option<Value>) -> Self {
        Self {
            validator,
            passed: true,
            expected,
            actual,
            error: None,
        }
    }
    
    /// Create a failed validation result
    pub fn failure(validator: String, expected: Option<Value>, actual: Option<Value>, error: String) -> Self {
        Self {
            validator,
            passed: false,
            expected,
            actual,
            error: Some(error),
        }
    }
}

impl Attachment {
    /// Create a new attachment
    pub fn new(attachment_type: AttachmentType, name: String, path: String, size: u64, mime_type: String) -> Self {
        Self {
            attachment_type,
            name,
            path,
            size,
            mime_type,
            created_at: now(),
        }
    }
    
    /// Create a screenshot attachment
    pub fn screenshot(name: String, path: String, size: u64) -> Self {
        Self::new(AttachmentType::Screenshot, name, path, size, "image/png".to_string())
    }
    
    /// Create a log attachment
    pub fn log(name: String, path: String, size: u64) -> Self {
        Self::new(AttachmentType::Log, name, path, size, "text/plain".to_string())
    }
}

impl StepContext {
    /// Create a new step context
    pub fn new(test_case_id: Uuid, session_id: Uuid) -> Self {
        Self {
            variables: HashMap::new(),
            http_client: reqwest::Client::new(),
            test_case_id,
            session_id,
            start_time: now(),
        }
    }
    
    /// Get a variable value
    pub fn get_variable(&self, key: &str) -> Option<&Value> {
        self.variables.get(key)
    }
    
    /// Set a variable value
    pub fn set_variable(&mut self, key: String, value: Value) {
        self.variables.insert(key, value);
    }
    
    /// Update variables from a map
    pub fn update_variables(&mut self, vars: ValueMap) {
        self.variables.extend(vars);
    }
    
    /// Parse a template string with variables
    pub fn parse_template(&self, template: &str) -> Result<String> {
        // TODO: Implement template parsing with variable substitution
        // This will be similar to HttpRunner's Parser functionality
        Ok(template.to_string())
    }
}

impl std::fmt::Display for StepType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            StepType::Request => write!(f, "request"),
            StepType::API => write!(f, "api"),
            StepType::TestCase => write!(f, "testcase"),
            StepType::Transaction => write!(f, "transaction"),
            StepType::Rendezvous => write!(f, "rendezvous"),
            StepType::ThinkTime => write!(f, "thinktime"),
            StepType::WebSocket => write!(f, "websocket"),
            StepType::Android => write!(f, "android"),
            StepType::IOS => write!(f, "ios"),
            StepType::Harmony => write!(f, "harmony"),
            StepType::Browser => write!(f, "browser"),
            StepType::Shell => write!(f, "shell"),
            StepType::Function => write!(f, "function"),
            StepType::AIGenerated => write!(f, "ai_generated"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_step_result_creation() {
        let id = Uuid::new_v4();
        let result = StepResult::new(id, "test_step".to_string(), StepType::Request);
        
        assert_eq!(result.id, id);
        assert_eq!(result.name, "test_step");
        assert_eq!(result.step_type, StepType::Request);
        assert!(!result.success);
    }

    #[test]
    fn test_step_result_builder() {
        let id = Uuid::new_v4();
        let result = StepResult::new(id, "test_step".to_string(), StepType::Request)
            .success()
            .with_elapsed(100)
            .with_data(serde_json::json!({"status": "ok"}));
        
        assert!(result.success);
        assert_eq!(result.elapsed, 100);
        assert!(result.data.is_some());
    }

    #[test]
    fn test_validation_result() {
        let success = ValidationResult::success(
            "status_code".to_string(),
            Some(serde_json::json!(200)),
            Some(serde_json::json!(200)),
        );
        assert!(success.passed);
        
        let failure = ValidationResult::failure(
            "status_code".to_string(),
            Some(serde_json::json!(200)),
            Some(serde_json::json!(404)),
            "Status code mismatch".to_string(),
        );
        assert!(!failure.passed);
    }

    #[test]
    fn test_step_context() {
        let test_case_id = Uuid::new_v4();
        let session_id = Uuid::new_v4();
        let mut context = StepContext::new(test_case_id, session_id);
        
        context.set_variable("test_var".to_string(), serde_json::json!("test_value"));
        
        assert_eq!(
            context.get_variable("test_var"),
            Some(&serde_json::json!("test_value"))
        );
    }
}
